
#include "stdafx.h"

#include "BridgeWrapper.h"

#include "STP.h"
#include "bridge.h"
#include "BaseTypesWrappers.h"

using namespace System::Diagnostics;
using namespace System::Text;

// ============================================================================

Bridge::QueuedBpdu::QueuedBpdu (int sizeToAlloc, int portIndex)
{
	this->bpdu = (unsigned char*) malloc (sizeToAlloc);
	this->portIndex = portIndex;
	this->size = sizeToAlloc;
}

Bridge::QueuedBpdu::~QueuedBpdu ()
{
	free (bpdu);
}

// ============================================================================

void Bridge::OnMstpLibEnableLearning (BRIDGE* bridge, int portIndex, int mstid, bool enable)
{
}

void Bridge::OnMstpLibEnableForwarding (BRIDGE* bridge, int portIndex, int mstid, bool enable)
{
}

void* Bridge::OnMstpLibTransmitGetBuffer (BRIDGE* bridge, int portIndex, unsigned int bpduSize)
{
	Bridge^ wrapper = GetBridgeFromRef (bridge);

	QueuedBpdu^ queuedBpdu = gcnew QueuedBpdu (bpduSize, portIndex);

	wrapper->queuedBpdus->Enqueue (queuedBpdu);

	return queuedBpdu->bpdu;
}

void Bridge::OnMstpLibTransmitReleaseBuffer (BRIDGE* bridge)
{
}

void Bridge::OnMstpLibDebugStrOut (BRIDGE* bridge, int portIndex, int treeIndex, const char* sz)
{
	String^ str = gcnew String (sz);

	Bridge^ wrapper = GetBridgeFromRef (bridge);
	wrapper->logSections->Add (gcnew LogSection (portIndex, treeIndex, str));
	wrapper->debugStrOutCallback (wrapper, portIndex, treeIndex, str);

	Console::Write (str);
}

void* Bridge::OnMstpLibAlloc (unsigned long size)
{
	void* result = malloc (size);
	memset (result, 0, size);
	return result;
}

void Bridge::OnMstpLibFree (void* p)
{
	free (p);
}

void Bridge::OnMstpLibFdbFlush (BRIDGE* bridge, int portIndex, int treeIndex)
{
}

// ============================================================================

Bridge^ Bridge::GetBridgeFromRef (BRIDGE* bridgeUnmanaged)
{
	for each (Bridge^ bridge in bridges)
	{
		if (bridge->bridge == bridgeUnmanaged)
			return bridge;
	}

	Debug::Assert (false);
	return nullptr;
}

// ============================================================================

static const STP_CALLBACKS mstpCallbacks = 
{
	Bridge::OnMstpLibEnableLearning,
	Bridge::OnMstpLibEnableForwarding,
	Bridge::OnMstpLibTransmitGetBuffer,
	Bridge::OnMstpLibTransmitReleaseBuffer,
	Bridge::OnMstpLibFdbFlush,
	Bridge::OnMstpLibAlloc,
	Bridge::OnMstpLibFree,
	Bridge::OnMstpLibDebugStrOut,
};

// ============================================================================

BridgeTree::BridgeTree (BRIDGE_TREE* bridgeTree, Bridge^ bridgeWrapper, int treeIndex)
{
	this->tree = bridgeTree;
	this->BridgeWrapper = bridgeWrapper;
	this->TreeIndex = treeIndex;
}

// ============================================================================

Bridge::Bridge (BinaryReader^ br,
						int portCount,
						int treeCount,
						DebugStrOutCallback^ debugStrOutCallback,
						BpduTransmitCallback^ bpduTransmitCallback)
{
	static unsigned char macAddressLastByte = 1;

	if (br == nullptr)
	{
		this->x = 0;
		this->y = 0;
		this->width = (5 + Port::InteriorDrawingLongSize + 5) * portCount;
		this->height = 50;
	}
	else
	{
		throw gcnew NotImplementedException ();
		/*
		portCount = br->ReadInt32 ();
		treeCount = br->ReadInt32 ();
		this->x = br->ReadSingle ();
		this->y = br->ReadSingle ();
		this->width = br->ReadSingle ();
		this->height = br->ReadSingle ();
		this->userRef = br->ReadUInt32 ();
		*/
	}

	this->bridge = STP_CreateBridge (portCount, treeCount, &mstpCallbacks, STP_MODE_802_1w_2001, 10000);

	this->debugStrOutCallback = debugStrOutCallback;
	this->bpduTransmitCallback = bpduTransmitCallback;

	this->queuedBpdus = gcnew Queue<QueuedBpdu^> ();

	bridgeTrees = gcnew array<BridgeTree^> (treeCount);
	for (int treeIndex = 0; treeIndex < treeCount; treeIndex++)
		bridgeTrees [treeIndex] = gcnew BridgeTree (bridge->trees [treeIndex], this, treeIndex);

	ports = gcnew array<Port^> (portCount);
	for (int portIndex = 0; portIndex < portCount; portIndex++)
		ports [portIndex] = gcnew Port (br, bridge, portIndex);

	bridges->Add (this);

	STP_EnableLogging (bridge, true);

	Debug::Assert (macAddressLastByte < 250);
	unsigned char bridgeAddress [6] = { 0x00, 0x55, 0x55, 0xAA, 0xAA, macAddressLastByte };
	macAddressLastByte++;

	STP_OnBridgeAddressChanged (this->bridge, bridgeAddress, System::Environment::TickCount);
}

void Bridge::Serialize (BinaryWriter^ bw)
{
	throw gcnew NotImplementedException ();
	/*
	bw->Write (bridge->portCount);
	bw->Write (bridge->treeCount);
	bw->Write (x);
	bw->Write (y);
	bw->Write (width);
	bw->Write (height);
	bw->Write (userRef);

	for (int i = 0; i < bridge->portCount; i++)
		ports [i]->Serialize (bw);
	*/
}

// ============================================================================

Bridge::~Bridge ()
{
	STP_DestroyBridge (bridge);
}

// ============================================================================

int BridgeTree::rootPortId::get ()
{
	return tree->rootPortId;
}

BridgeId^ BridgeTree::BridgeIdentifier::get ()
{
	return gcnew BridgeId (&tree->GetBridgeIdentifier ());
}

unsigned short BridgeTree::Priority::get ()
{
	return STP_GetBridgePriority (this->BridgeWrapper->bridge, this->TreeIndex);
}

void BridgeTree::Priority::set (unsigned short value)
{
	STP_SetBridgePriority (this->BridgeWrapper->bridge, this->TreeIndex, value, System::Environment::TickCount);
}


// ============================================================================

int Bridge::PortCount::get ()
{
	return bridge->portCount;
}

// ============================================================================

bool Bridge::Started::get ()
{
	return bridge->started;
}

// ============================================================================

void Bridge::OnBpduReceived (const unsigned char* bpdu, unsigned int bpduSize, int portIndex)
{
	STP_OnBpduReceived (bridge, portIndex, bpdu, bpduSize, System::Environment::TickCount);
	TransmitQueuedBpdus ();
}

// ============================================================================

void Bridge::OnPortEnabled (int portIndex, unsigned long linkSpeedMegabitsPerSecond, bool fullDuplex)
{
	DUPLEX duplex = fullDuplex ? FULL_DUPLEX : HALF_DUPLEX;

	STP_OnPortEnabled (bridge, portIndex, linkSpeedMegabitsPerSecond, duplex, System::Environment::TickCount);

	TransmitQueuedBpdus ();
}

// ============================================================================

void Bridge::OnPortDisabled (int portIndex)
{
	STP_OnPortDisabled (bridge, portIndex, System::Environment::TickCount);
	TransmitQueuedBpdus ();
}

// ============================================================================

void Bridge::OnTick ()
{
	STP_OnOneSecondTick (bridge, System::Environment::TickCount);
	TransmitQueuedBpdus ();
}

// ============================================================================

void Bridge::Start ()
{
	STP_StartBridge (bridge, System::Environment::TickCount);
}

// ============================================================================

void Bridge::Stop ()
{
	STP_StopBridge (bridge, System::Environment::TickCount);
}

// ============================================================================

String^ Bridge::GetBacklog (int portIndex, int treeIndex)
{
	StringBuilder^ sb = gcnew StringBuilder ();

	for each (LogSection^ section in logSections)
	{
		if ((portIndex == -1 || section->portIndex == -1 || portIndex == section->portIndex)
			&& (treeIndex == -1 || section->treeIndex == -1 || treeIndex == section->treeIndex))
		{
			sb->Append (section->str);
		}
	}

	return sb->ToString ();
}

// ============================================================================

void Bridge::TransmitQueuedBpdus ()
{
	while (queuedBpdus->Count > 0)
	{
		QueuedBpdu^ qb = queuedBpdus->Dequeue ();

		bpduTransmitCallback (this, qb->portIndex, qb->bpdu, qb->size);

		delete qb;
	}
}

// ============================================================================

PointF Bridge::GetCoordsForExteriorPort (int portIndex)
{
	Port^ port = ports [portIndex];

	if (port->side == Port::Side::Bottom)
	{
		return PointF (x + port->offset, y + height + Port::ExteriorDrawingHeight);
	}
	else if (port->side == Port::Side::Left)
	{
		return PointF (x - Port::ExteriorDrawingHeight + 1, y + port->offset);
	}
	else if (port->side == Port::Side::Top)
	{
		return PointF (x + port->offset, y - Port::ExteriorDrawingHeight + 1);
	}
	else // if (port->side == Port::Side::Right)
	{
		return PointF (x + width + Port::ExteriorDrawingHeight, y + port->offset);
	}
}

// ============================================================================

void Bridge::Paint (Graphics^ g, bool selected, int selectedPortIndex)
{
	// bridge rectangle
	if (selected && (selectedPortIndex == -1))
		g->DrawRectangle (selectedBridgePen, x + 1, y + 1, width - 1, height - 1);
	else
		g->DrawRectangle (SystemPens::WindowText, x, y, width, height);

	// ports
	for (int portIndex = 0; portIndex < ports->Length; portIndex++)
	{
		Port^ port = ports [portIndex];

		// interior port rectangle
		Pen^ portRectanglePen;
		if (selected && (portIndex == selectedPortIndex))
			portRectanglePen = selectedPortPen;
		else
			portRectanglePen = SystemPens::WindowText;

		PortRole role = port->trees [CIST_INDEX]->role;
		bool learning = port->trees [CIST_INDEX]->learning;
		bool forwarding = port->trees [CIST_INDEX]->forwarding;
		bool operEdge = port->operEdge;

		// Draw the interior of the port.
		RectangleF r = GetRectForInteriorPort (portIndex);
		if (selected && selectedPortIndex == portIndex)
		{
			r.X++; r.Width--;
			r.Y++; r.Height--;
		}
		g->DrawRectangle (portRectanglePen, r.X, r.Y, r.Width, r.Height);

		// Draw the exterior of the port.
		Bitmap^ bitmap;
		switch (port->side)
		{
			case Port::Side::Bottom:
				bitmap = port->GetBitmap (CIST_INDEX, RotateFlipType::RotateNoneFlipNone);
				g->DrawImage (bitmap, x + port->offset - bitmap->Width / 2, y + height);
				break;

			case Port::Side::Left:
				bitmap = port->GetBitmap (CIST_INDEX, RotateFlipType::Rotate90FlipNone);
				g->DrawImage (bitmap, x - Port::ExteriorDrawingHeight, y + port->offset - bitmap->Height / 2);
				break;

			case Port::Side::Top:
				bitmap = port->GetBitmap (CIST_INDEX, RotateFlipType::Rotate180FlipNone);
				g->DrawImage (bitmap, x + port->offset - bitmap->Width / 2, y - Port::ExteriorDrawingHeight);
				break;

			case Port::Side::Right:
				bitmap = port->GetBitmap (CIST_INDEX, RotateFlipType::Rotate270FlipNone);
				g->DrawImage (bitmap, x + width, y + port->offset - bitmap->Height / 2);
				break;
		}
	}

	g->DrawLine (SystemPens::WindowText, x, y + 5, x + 5, y);
	g->DrawLine (SystemPens::WindowText, x, y + 8, x + 8, y);
	g->DrawLine (SystemPens::WindowText, x, y + 11, x + 11, y);

	g->DrawLine (SystemPens::WindowText, x + width - 5, y, x + width, y + 5);
	g->DrawLine (SystemPens::WindowText, x + width - 8, y, x + width, y + 8);
	g->DrawLine (SystemPens::WindowText, x + width - 11, y, x + width, y + 11);

	g->DrawLine (SystemPens::WindowText, x, y + height - 5, x + 5, y + height);
	g->DrawLine (SystemPens::WindowText, x, y + height - 8, x + 8, y + height);
	g->DrawLine (SystemPens::WindowText, x, y + height - 11, x + 11, y + height);

	g->DrawLine (SystemPens::WindowText, x + width - 5, y + height, x + width, y + height - 5);
	g->DrawLine (SystemPens::WindowText, x + width - 8, y + height, x + width, y + height - 8);
	g->DrawLine (SystemPens::WindowText, x + width - 11, y + height, x + width, y + height - 11);

	Brush^ brush;
	if (bridge->started)
		brush = Brushes::Green;
	else
		brush = Brushes::DarkGray;

	g->FillEllipse (brush, x + 2, y + 2, (float) 15, (float) 15);
}

// ============================================================================

void Bridge::SerializeBridges (BinaryWriter^ bw)
{
	throw gcnew NotImplementedException ();
	/*
	bw->Write (nextBridgeUserRef);

	bw->Write (bridges->Count);

	for (int i = 0; i < bridges->Count; i++)
		bridges [i]->Serialize (bw);
	*/
}

void Bridge::DeserializeBridges (BinaryReader^ br,
								 DebugStrOutCallback^ debugStrOutCallback,
								 BpduTransmitCallback^ bpduTransmitCallback)
{
	throw gcnew NotImplementedException ();
	/*
	nextBridgeUserRef = br->ReadUInt32 ();

	bridges->Clear ();
	logSections->Clear ();

	int bridgeCount = br->ReadInt32 ();
	for (int i = 0; i < bridgeCount; i++)
	{
		Bridge^ bridge = gcnew Bridge (br, -1, -1, debugStrOutCallback, bpduTransmitCallback);
	}
	*/
}

// ============================================================================

void Bridge::SetCoordsForInteriorPort (int portIndex, float mouseX, float mouseY)
{
	assert (width > height); // all code below count on this condition

	mouseX -= x;
	mouseY -= y;

	if ((-mouseX <= mouseY - height) && (mouseY >= height / 2) && (mouseX - width <= mouseY - height))
	{
		// bottom side
		ports [portIndex]->side = Port::Side::Bottom;

		if (mouseX < Port::InteriorDrawingLongSize / 2 + 1)
			ports [portIndex]->offset = Port::InteriorDrawingLongSize / 2 + 1;
		else if (mouseX > width - Port::InteriorDrawingLongSize / 2)
			ports [portIndex]->offset = width - Port::InteriorDrawingLongSize / 2;
		else
			ports [portIndex]->offset = mouseX;
	}
	else if ((-mouseX >= mouseY - height) && (mouseX <= mouseY))
	{
		// left side
		ports [portIndex]->side = Port::Side::Left;

		if (mouseY < Port::InteriorDrawingLongSize / 2)
			ports [portIndex]->offset = Port::InteriorDrawingLongSize / 2;
		else if (mouseY > height - Port::InteriorDrawingLongSize / 2)
			ports [portIndex]->offset = height - Port::InteriorDrawingLongSize / 2;
		else
			ports [portIndex]->offset = mouseY;
	}
	else if ((mouseX >= mouseY) && (mouseY <= height / 2) && (mouseX - width <= -mouseY))
	{
		// top side
		ports [portIndex]->side = Port::Side::Top;

		if (mouseX < Port::InteriorDrawingLongSize / 2)
			ports [portIndex]->offset = Port::InteriorDrawingLongSize / 2;
		else if (mouseX > width - Port::InteriorDrawingLongSize / 2)
			ports [portIndex]->offset = width - Port::InteriorDrawingLongSize / 2;
		else
			ports [portIndex]->offset = mouseX;
	}
	else
	{
		// right side
		ports [portIndex]->side = Port::Side::Right;

		if (mouseY < Port::InteriorDrawingLongSize / 2)
			ports [portIndex]->offset = Port::InteriorDrawingLongSize / 2;
		else if (mouseY > height - Port::InteriorDrawingLongSize / 2)
			ports [portIndex]->offset = height - Port::InteriorDrawingLongSize / 2;
		else
			ports [portIndex]->offset = mouseY;
	}
}

// ============================================================================

RectangleF Bridge::GetRectForInteriorPort (int portIndex)
{
	Port^ port = ports [portIndex];

	RectangleF r;

	if (port->side == Port::Side::Bottom)
	{
		r.X = x + port->offset - Port::InteriorDrawingLongSize / 2;
		r.Y = y + height - Port::InteriorDrawingShortSize;
		r.Width = Port::InteriorDrawingLongSize;
		r.Height = Port::InteriorDrawingShortSize;
	}
	else if (port->side == Port::Side::Left)
	{
		r.X = x,
		r.Y = y + port->offset - Port::InteriorDrawingLongSize / 2,
		r.Width = Port::InteriorDrawingShortSize,
		r.Height = Port::InteriorDrawingLongSize;
	}
	else if (port->side == Port::Side::Top)
	{
		r.X = x + port->offset - Port::InteriorDrawingLongSize / 2;
		r.Y = y;
		r.Width = Port::InteriorDrawingLongSize;
		r.Height = Port::InteriorDrawingShortSize;
	}
	else // if (side == Port::Side::Right)
	{
		r.X = x + width - Port::InteriorDrawingShortSize,
		r.Y = y + port->offset - Port::InteriorDrawingLongSize / 2,
		r.Width = Port::InteriorDrawingShortSize,
		r.Height = Port::InteriorDrawingLongSize;
	}

	return r;
}

